/*******************************************************************************

  Pilot Intelligence Library
    https://gitee.com/pi-lab

*******************************************************************************/

#include <iostream>
#include <string>

#include "utils.h"
#include "utils_debug.h"
#include "Socket++.h"
#include "ComManager.h"
#include "GSLAM/hal/HAL_UART.h"

namespace pi {


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

class ComManager_InnerData
{
public:
    ComManager_InnerData() {
        m_comOpened = 0;
    }
    virtual ~ComManager_InnerData() {
        m_comOpened = 0;
    }

    enum ComType {
        CT_UART,
        CT_UDP,
        CT_TCP
    };


public:
    int             m_comOpened;
    UART            m_uart;
    pi::RSocket     m_socket;
    ComType         m_comType;
};



////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

ComManager::ComManager()
{
    m_priData = std::make_shared<ComManager_InnerData>();
}

ComManager::~ComManager()
{

}

int ComManager::open(const std::string& com_name, const std::string& out_name)
{
    std::string cn = com_name;
    cn = str_tolower(cn);
    int ret = 0;

    m_priData->m_comOpened = 0;

    if( "udp" == cn.substr(0, 3) || "tcp" == cn.substr(0, 3) ) {
        StringArray sa = split_text(cn, ":");
        if( sa.size() != 3 ) {
            dbg_pe("Input com_name [%s] is wrong! ex: udp:192.168.1.1:14550\n", com_name.c_str());
            return -1;
        }

        if ( sa[0] == "udp" ) {
            m_priData->m_comType = ComManager_InnerData::CT_UDP;

            ret = m_priData->m_socket.startServer(str_to_int(sa[2]), pi::SOCKET_UDP);
            if( 0 == ret ) m_priData->m_socket.setNonBlocking(1);
        } else if ( sa[0] == "udpclient" ) {
            m_priData->m_comType = ComManager_InnerData::CT_UDP;
            ret = m_priData->m_socket.startClient(sa[1], str_to_int(sa[2]), pi::SOCKET_UDP);
            if( 0 == ret ) m_priData->m_socket.setNonBlocking(1);
            m_priData->m_socket.send("hello");
        } else if ( sa[0] == "tcp" ) {
            m_priData->m_comType = ComManager_InnerData::CT_TCP;

            ret = m_priData->m_socket.startServer(str_to_int(sa[2]));
            if( 0 == ret ) m_priData->m_socket.setNonBlocking(1);
        } else if ( sa[0] == "tcpclient" ) {
            m_priData->m_comType = ComManager_InnerData::CT_TCP;

            ret = m_priData->m_socket.startClient(sa[1], str_to_int(sa[2]), pi::SOCKET_TCP);
            if( 0 == ret ) m_priData->m_socket.setNonBlocking(1);
        }else if ( sa[0] == "udpmultcast" ) {
            m_priData->m_comType = ComManager_InnerData::CT_UDP;
            ret = m_priData->m_socket.startClient(sa[1], str_to_int(sa[2]), pi::SOCKET_UDP_MULTICAST);
            if( 0 == ret ) {
                m_priData->m_socket.setNonBlocking(1);
                std::string on = out_name;
                on = str_tolower(on);
                StringArray so = split_text(on, ":");
                m_priData->m_socket.setRemoteAddr(so[0],str_to_int(so[1]));
            }
        }

        if( ret != 0 ) {
            dbg_pe("Open sokcet failed, please check com_name [%s]\n", com_name.c_str());
            return ret;
        }

        m_priData->m_comOpened = 1;

        return 0;
    } else {
        m_priData->m_comType = ComManager_InnerData::CT_UART;

        int baud_rate = 115200;
        StringArray sa = split_text(com_name, ",");
        if( sa.size() > 1 ) baud_rate = str_to_int(sa[1]);

        strcpy(m_priData->m_uart.port_name, sa[0].c_str());
        m_priData->m_uart.baud_rate = baud_rate;

        ret = m_priData->m_uart.open();
        if( ret == 0 ) {
            m_priData->m_comOpened = 1;
        }

        return ret;
    }
}

int ComManager::close(void)
{
    ComManager_InnerData::ComType ct = m_priData->m_comType;

    m_priData->m_comOpened = 0;

    if( ct == ComManager_InnerData::CT_UART ) {
        return m_priData->m_uart.close();
    } else if ( ct == ComManager_InnerData::CT_TCP || ct == ComManager_InnerData::CT_UDP ) {
        m_priData->m_socket.close();

        return 0;
    } else {
        return -1;
    }
}

int ComManager::isOpen(void)
{
    return m_priData->m_comOpened;
}


int ComManager::write(char *d, int len)
{
    ComManager_InnerData::ComType ct = m_priData->m_comType;

    if( ct == ComManager_InnerData::CT_UART ) {
        return m_priData->m_uart.write(d, len);
    } else if ( ct == ComManager_InnerData::CT_TCP || ct == ComManager_InnerData::CT_UDP ) {
        len = m_priData->m_socket.send((char*) d, len);
        return len;
    } else {
        return 0;
    }
}

int ComManager::read(char *d,  int len)
{
    ComManager_InnerData::ComType ct = m_priData->m_comType;

    if( ct == ComManager_InnerData::CT_UART ) {
        return m_priData->m_uart.read(d, len);
    } else if ( ct == ComManager_InnerData::CT_TCP || ct == ComManager_InnerData::CT_UDP ) {
        int n = 0;
        n = m_priData->m_socket.recv(d, len);
        return n;
    } else {
        return 0;
    }
}

} // end of namespace pi

